package config

import (
	"io"
	"log/slog"
	"os"
	"path/filepath"
	"slices"
	"strings"
	"sync"
	"sync/atomic"

	"gitee.com/gousing/gerror"
	"github.com/fsnotify/fsnotify"
	"github.com/go-viper/mapstructure/v2"
	"github.com/spf13/viper"
)

// defaultConfig 配置实例
//   - Golang Config 基于[viper](https://github.com/spf13/viper)包装的单实例配置管理器, 支持多文件监听(热更新)
//   - 1. 注意：加载多个配置文件，请使用 config.LoadFromXXX() 代理方法，相同Key后加载的配置文件将会覆盖前面的配置
//   - 2. 注意：INI格式配置文件一级配置项Viper默认添加一个命名为default父级节点，INI格式配置文件一级配置项只能通过default.XXX进行读取
//   - 3. 注意: 如需监听配置，请使用 config.OnChange() 代理方法：
//   - 原因 1：如加载了多个配置文件, 使用Viper.OnConfigChange => WatchConfig, 仅能监听最后加载的那个配置文件
//   - 原因 2：如加载了多个配置文件, 使用Viper.OnConfigChange => WatchConfig, 将导致多个配置文件项丢失(Viper内部使用ReadInConfig，而非MergeInConfig)
//   - 配置项获取基于[Gousing Values](https://gitee.com/gousing/values) 进行了包装(支持GetBoolX/GetStringX/GetIntX/GetUintX/GetFloatX/GetSliceX/GetMapX),  每个数据类型提供三种接口:
//   - 1. config.GetXXXFunc() (T) 读取并转换配置项，忽略错误(异常时默认值为零值)
//   - 2. config.GetXXXFuncD() (T) 读取并转换配置项，指定默认值(异常时默认值为指定值)
//   - 3. config.GetXXXFuncE() (T,Error) 读取并转换配置项，获取错误信息
type config struct {
	files     map[string]int64 // 记录已成功加载的配置文件清单, 值为文件修改时间(用于解决Windows文件Event事件两次触发的问题)
	onChanges []func(e fsnotify.Event)
	watchDirs map[string]struct{} // 记录已监听的配置文件路径清单(避免重复监听)
	watchOnce sync.Once
	watcher   *fsnotify.Watcher
}

var (
	defaultMu                                     = new(sync.RWMutex)
	defaultViper                                  = atomic.Pointer[viper.Viper]{}
	defaultOnce                                   = new(sync.Once)
	defaultOpts                                   = []viper.Option{}
	defaultDecodeHook mapstructure.DecodeHookFunc = nil
	defaultConfig                                 = &config{files: make(map[string]int64), watchDirs: make(map[string]struct{})}
)
var (
	ErrKeyNotExist = gerror.Defined("config: key does not exist")
)

func initDeafultViper() {
	opts := make([]viper.Option, 0, len(defaultOpts)+2)
	opts = append(opts, defaultOpts...)
	opts = append(opts, viper.WithLogger(GetLogger()), viper.WithDecodeHook(defaultDecodeHook))
	defaultViper.Store(viper.NewWithOptions(opts...))
}

func deafult() *viper.Viper {
	defaultOnce.Do(func() {
		if defaultViper.Load() == nil {
			initDeafultViper()
		}
	})
	return defaultViper.Load()
}

// SetViperDecodeHook 自定义 Viper 初始化DecodeHook
//   - 会触发 initDeafultViper 初始化单前实例
func SetViperDecodeHook(hook mapstructure.DecodeHookFunc) {
	defaultMu.Lock()
	defer defaultMu.Unlock()
	defaultDecodeHook = hook
	initDeafultViper()
}

// SetViperOptions 自定义 Viper 初始化选项
//   - 会触发 initDeafultViper 初始化单前实例
func SetViperOptions(opts ...viper.Option) {
	defaultMu.Lock()
	defer defaultMu.Unlock()
	if len(opts) > 0 {
		defaultOpts = opts
	} else {
		defaultOpts = []viper.Option{}
	}
	initDeafultViper()
}

// LoadFromFiles 加载配置文件到单前实例
//   - 合并方式加载多个配置文件或多次加载文件，相同Key, 后读取的配置项将会覆盖前面的配置
func LoadFromFiles(files ...string) error {
	defaultMu.Lock()
	defer defaultMu.Unlock()
	return _loadFromFiles(files...)
}
func _loadFromFiles(files ...string) (errs error) {
	for _, file := range files {
		file = filepath.Clean(file)
		if file == "" {
			continue
		}
		// 是否已加载过文件
		//if _, ok := defaultConfig.files[file]; ok {
		//continue
		//}
		if info, err := os.Stat(file); err != nil {
			errs = gerror.WrapOrNewF(errs, "the file[%s] read failed, %s. ", file, err)
			continue
		} else if info.IsDir() {
			errs = gerror.WrapOrNewF(errs, "the file[%s] is a directory, not a file", file)
			continue
		} else {
			defaultConfig.files[file] = info.ModTime().Unix()
		}
		fileExt := filepath.Ext(file)
		if len(fileExt) < 2 {
			errs = gerror.WrapOrNewF(errs, "the file[%s] not has ext", file)
			continue
		}
		deafult().SetConfigFile(file)
		deafult().SetConfigType(fileExt[1:])
		if err := deafult().MergeInConfig(); err != nil {
			errs = gerror.WrapOrNewF(errs, "the file[%s] MergeInConfig failed, %s. ", file, err)
		} else {
			GetLogger().Info("config:LoadFromFiles loaded", slog.String("file", file))
		}
	}
	if errs != nil {
		GetLogger().Error("config:LoadFromFiles failed", slog.String("error", errs.Error()))
		errs = gerror.WrapF(errs, "config:LoadFromFiles failed")
	}
	return errs
}

// LoadFromReader 读取配置文件到单前实例
//   - 合并方式读取多个配置文件或多次读取文件，相同Key, 后读取的配置项将会覆盖前面的配置
//   - 为nil的 io.Reader 将忽略
//   - configType: "json", "toml", "yaml", "yml", "properties", "props", "prop", "hcl", "tfvars", "dotenv", "env", "ini"
func LoadFromReader(configType string, files ...io.Reader) (errs error) {
	defaultMu.Lock()
	defer defaultMu.Unlock()
	return _loadFromReader(configType, files...)
}
func _loadFromReader(configType string, readers ...io.Reader) (errs error) {
	configType = strings.ToLower(configType)
	if !slices.Contains(viper.SupportedExts, configType) {
		errs = gerror.NewF("configType[%s] is not supported, supported: json/toml/yaml/yml/properties/props/prop/hcl/tfvars/dotenv/env/ini", configType)
		GetLogger().Error("config:LoadFromReader failed", slog.String("error", errs.Error()))
		errs = gerror.WrapF(errs, "config:LoadFromReader failed")
		return errs
	}
	if len(readers) == 0 {
		return nil
	}
	deafult().SetConfigType(configType)
	for _, reader := range readers {
		if reader == nil {
			continue
		}
		if err := deafult().MergeConfig(reader); err != nil {
			errs = gerror.WrapOrNewF(errs, "the io.Reader[%s] failed, %s. ", reader, err)
		} else {
			GetLogger().Info("config:LoadFromReader loaded", slog.Any("io.Reader", reader))
		}
	}
	if errs != nil {
		GetLogger().Error("config:LoadFromReader failed", slog.String("error", errs.Error()))
		errs = gerror.WrapF(errs, "config:LoadFromReader failed")
	}
	return errs
}

// LoadFromMaps 加载多个 map[string]any配置项, 合并到单前实例
//   - 为空或nil的 map 将忽略
func LoadFromMaps(maps ...map[string]any) (errs error) {
	defaultMu.Lock()
	defer defaultMu.Unlock()
	for index, m := range maps {
		if len(m) == 0 {
			continue
		}
		if err := deafult().MergeConfigMap(m); err != nil {
			errs = gerror.WrapOrNewF(errs, "the index[%d] map Merge failed, %s. ", index, err)
		} else {
			GetLogger().Info("config:LoadFromMaps loaded", slog.Int("mapSize", len(m)))
		}
	}
	if errs != nil {
		errs = gerror.WrapF(errs, "config:LoadFromMaps failed")
		GetLogger().Error("config:LoadFromMaps failed", slog.String("error", errs.Error()))
	}
	return errs
}

// Reset 重置配置实例，将清空已加载(含设置)的全部配置信息及OnChange监听
func Reset() error {
	defaultMu.Lock()
	defer defaultMu.Unlock()
	return _reset()
}
func _reset() error {
	initDeafultViper()
	defaultConfig.files = make(map[string]int64)
	defaultConfig.watchDirs = make(map[string]struct{})
	if defaultConfig.watcher != nil {
		defaultConfig.watchOnce = sync.Once{}
		err := defaultConfig.watcher.Close()
		defaultConfig.watcher = nil
		if err != nil {
			GetLogger().Error("config:Reset failed", slog.String("error", err.Error()))
			return err
		}
	}
	return nil
}

// ResetAndLoadFromFiles 重置配置实例并重新加载配置文件到单前实例
//   - 重置配置实例，将清空已加载(含设置)的全部配置信息及OnChange监听
//   - 合并方式加载多个配置文件或多次加载文件，相同Key, 后读取的配置项将会覆盖前面的配置
func ResetAndLoadFromFiles(files ...string) error {
	defaultMu.Lock()
	defer defaultMu.Unlock()
	if err := _reset(); err != nil {
		return err
	}
	return _loadFromFiles(files...)
}

// ResetAndLoadFromReader 重置配置实例并重新读取配置文件到单前实例
//   - 重置配置实例，将清空已加载(含设置)的全部配置信息及OnChange监听
//   - 合并方式读取多个配置文件或多次读取文件，相同Key, 后读取的配置项将会覆盖前面的配置
//   - configType: "json", "toml", "yaml", "yml", "properties", "props", "prop", "hcl", "tfvars", "dotenv", "env", "ini"
func ResetAndLoadFromReader(configType string, files ...io.Reader) error {
	defaultMu.Lock()
	defer defaultMu.Unlock()
	if err := _reset(); err != nil {
		return err
	}
	return _loadFromReader(configType, files...)
}
